home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™ 1987-1994 / MacHack™ '93 / Hacks '93 / OK, What was that again? / Application / WhatWasThat.p < prev    next >
Text File  |  1993-06-18  |  21KB  |  699 lines

  1. PROGRAM Sample;
  2.  
  3. USES
  4.     MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, Traps, Notification,
  5. {my custom stuff}
  6.     Utilities;
  7.  
  8. CONST
  9.     {MPW 3.0 will include a Traps.p interface file that includes constants for trap numbers.
  10.      These constants are from that file.}
  11.     {1.02 - since using MPW 3.0 only, we include Traps.p, so we now have trap numbers.}
  12.  
  13.     {1.01 - changed constants to begin with 'k' for consistency, except for resource IDs}
  14.     {SysEnvironsVersion is passed to SysEnvirons to tell it which version of the
  15.      SysEnvRec we understand.}
  16.     kSysEnvironsVersion        = 1;
  17.  
  18.     {OSEvent is the event number of the suspend/resume and mouse-moved events sent
  19.      by MultiFinder. Once we determine that an event is an osEvent, we look at the
  20.      high byte of the message sent to determine which kind it is. To differentiate
  21.      suspend and resume events we check the resumeMask bit.}
  22.     kOSEvent                = app4Evt;    {event used by MultiFinder}
  23.     kSuspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  24.     kResumeMask                = 1;        {bit of message field for resume vs. suspend}
  25.     kNoEvents                = 0;        {no events mask}
  26.  
  27.     {1.01 - kMinHeap - This is the minimum result from the following
  28.      equation:
  29.             
  30.             ORD(GetApplLimit) - ORD(ApplicZone)
  31.             
  32.      for the application to run. It will insure that enough memory will
  33.      be around for reasonable-sized scraps, FKEYs, etc. to exist with the
  34.      application, and still give the application some 'breathing room'.
  35.      To derive this number, we ran under a MultiFinder partition that was
  36.      our requested minimum size, as given in the 'SIZE' resource.}
  37.      
  38.     kMinHeap    = 21 * 1024;
  39.     
  40.     {1.01 - kMinSpace - This is the minimum result from PurgeSpace, when called
  41.      at initialization time, for the application to run. This number acts
  42.      as a double-check to insure that there really is enough memory for the
  43.      application to run, including what has been taken up already by
  44.      pre-loaded resources, the scrap, code, and other sundry memory blocks.}
  45.      
  46.     kMinSpace    = 8 * 1024;
  47.     
  48.     {kExtremeNeg and kExtremePos are used to set up wide open rectangles and regions.}
  49.     kExtremeNeg    = -32768;
  50.     kExtremePos    = 32767 - 1;            {required for old region bug}
  51.     
  52.     {The following constants are all resource IDs, corresponding to resources in Sample.r.}
  53.     rMenuBar    = 128;                    {application's menu bar}
  54.     rAboutAlert    = 128;                    {about alert}
  55.     rUserAlert    = 129;                    {error user alert}
  56.     rNotInstalledAlert = 130;            {the extension isn’t installed}
  57.  
  58.     {The following constants are used to identify menus and their items. The menu IDs
  59.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  60.     mApple        = 128;                    {Apple menu}
  61.     iAbout        = 1;
  62.  
  63.     mFile        = 129;                    {File menu}
  64.     iNew        = 1;
  65.     iClose        = 4;
  66.     iQuit        = 12;
  67.  
  68.     mEdit        = 130;                    {Edit menu}
  69.     iUndo        = 1;
  70.     iCut        = 3;
  71.     iCopy        = 4;
  72.     iPaste        = 5;
  73.     iClear        = 6;
  74.  
  75.     {1.01 - kDITop and kDILeft are used to locate the Disk Initialization dialogs.}
  76.     kDITop        = $0050;
  77.     kDILeft        = $0070;
  78.  
  79.  
  80. VAR
  81.     {The "g" prefix is used to emphasize that a variable is global.}
  82.  
  83.     {GMac is used to hold the result of a SysEnvirons call. This makes
  84.      it convenient for any routine to check the environment. It is
  85.      global information, anyway.}
  86.     gMac                : SysEnvRec;    {set up by Initialize}
  87.  
  88.     {GHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
  89.      trap is available. If it is false, we know that we must call GetNextEvent.}
  90.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  91.  
  92.     {GInBackground is maintained by our osEvent handling routines. Any part of
  93.      the program can check it to find out if it is currently in the background.}
  94.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  95.  
  96.     gNotifyRec:            NMRec;
  97.  
  98. {$S Initialize}
  99. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  100.  
  101. {Check to see if a given trap is implemented. This is only used by the
  102.  Initialize routine in this program, so we put it in the Initialize segment.
  103.  The recommended approach to see if a trap is implemented is to see if
  104.  the address of the trap routine is the same as the address of the
  105.  Unimplemented trap.}
  106. {1.02 - Needs to be called after call to SysEnvirons so that it can check
  107.  if a ToolTrap is out of range of a pre-MacII ROM.}
  108.  
  109. BEGIN
  110.     IF (tType = ToolTrap) &
  111.         (gMac.machineType > envMachUnknown) &
  112.         (gMac.machineType < envMacII) THEN BEGIN        {it's a 512KE, Plus, or SE}
  113.         tNumber := BAND(tNumber, $03FF);
  114.         IF tNumber > $01FF THEN                            {which means the tool traps}
  115.             tNumber := _Unimplemented;                    {only go to $01FF}
  116.     END;
  117.     TrapAvailable := NGetTrapAddress(tNumber, tType) <>
  118.                         GetTrapAddress(_Unimplemented);
  119. END; {TrapAvailable}
  120.  
  121.  
  122. {$S Main}
  123. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  124.  
  125. {Check if a window belongs to a desk accessory.}
  126.  
  127. BEGIN
  128.     IF window = NIL THEN
  129.         IsDAWindow := FALSE
  130.     ELSE    {DA windows have negative windowKinds}
  131.         IsDAWindow := (WindowPeek(window)^.windowKind < 0);
  132. END; {IsDAWindow}
  133.  
  134.  
  135. {$S Main}
  136. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  137.  
  138. {Check to see if a window belongs to the application. If the window pointer
  139.  passed was NIL, then it could not be an application window. WindowKinds
  140.  that are negative belong to the system and windowKinds less than userKind
  141.  are reserved by Apple except for windowKinds equal to dialogKind, which
  142.  mean it is a dialog.
  143.  1.02 - In order to reduce the chance of accidentally treating some window
  144.  as an AppWindow that shouldn't be, we'll only return true if the windowkind
  145.  is userKind. If you add different kinds of windows to Sample you'll need
  146.  to change how this all works.}
  147.  
  148. BEGIN
  149.     IF window = NIL THEN
  150.         IsAppWindow := FALSE
  151.     ELSE    {application windows have windowKinds = userKind (8)}
  152.         WITH WindowPeek(window)^ DO
  153.             IsAppWindow := (windowKind = userKind);
  154. END; {IsAppWindow}
  155.  
  156.  
  157. {$S Main}
  158. PROCEDURE AlertUser;
  159.  
  160. {Display an alert that tells the user an error occurred, then exit the program.
  161.  This routine is used as an ultimate bail-out for serious errors that prohibit
  162.  the continuation of the application. Errors that do not require the termination
  163.  of the application should be handled in a different manner. Error checking and
  164.  reporting has a place even in the simplest application. For simplicity, the alert
  165.  displayed here only says that an error occurred, but not what it was. There are
  166.  various methods available for being more specific.}
  167.  
  168. VAR
  169.     itemHit    : INTEGER;
  170. BEGIN
  171.     SetCursor(arrow);
  172.     itemHit := Alert(rUserAlert, NIL);
  173.     ExitToShell;
  174. END; {AlertUser}
  175.  
  176. CONST
  177. SysZone = $2A6;                 {[GLOBAL VAR] Address of system heap zone
  178.    system heap zone [pointer]}
  179. ApplZone = $2AA;                {[GLOBAL VAR] Address of application heap zone
  180.    application heap zone [pointer]}
  181.  
  182. {$S Main}
  183. PROCEDURE DoCloseWindow(window: WindowPtr);
  184. BEGIN
  185.     SetZone(SystemZone);
  186.     KillPicture(GetWindowPic(window));
  187.     SetZone(ApplicationZone);
  188.     SetWindowPic(window,NIL);
  189.     DisposeWindow(window);
  190. END; {DoCloseWindow}
  191.  
  192.  
  193.  
  194. {$S Initialize}
  195. FUNCTION Gestalt(selector: OSType;VAR response: LONGINT): OSErr;
  196. EXTERNAL;
  197. FUNCTION NewGestalt(selector: OSType;gestaltFunction: ProcPtr): OSErr;
  198. EXTERNAL;
  199. FUNCTION ReplaceGestalt(selector: OSType;gestaltFunction: ProcPtr;VAR oldGestaltFunction: ProcPtr): OSErr;
  200. EXTERNAL;
  201.  
  202. TYPE
  203.  
  204. SharedGlobals = RECORD
  205.     q: QHdr;                {queue header}
  206.     nmReqPtr: NMRecPtr;        {notification manager record}
  207.     END;
  208. SharedGlobalsPtr = ^SharedGlobals;
  209.  
  210. VAR    
  211.     gSharedGlobals : SharedGlobalsPtr;
  212.  
  213. PROCEDURE RememberWhereMyQueueIs;
  214. VAR
  215.     err : OSErr;
  216.     itemHit    : INTEGER;
  217. BEGIN
  218. (*
  219.     Ptr(gQueueHeader) := NewPtrSys(sizeof(QHdr));
  220.  
  221.     with gQueueHeader^ do begin
  222.         qFlags:= 0;
  223.         qHead:= nil;
  224.         qTail:= nil;
  225.     end;
  226.  
  227.     err := NewGestalt('Msg!',ProcPtr(gQueueHeader));
  228. *)
  229.     err := Gestalt('Msg!',longint(gSharedGlobals));
  230.     if err <> noErr then begin
  231.         SetCursor(arrow);
  232.         itemHit := Alert(rNotInstalledAlert, NIL);
  233.         ExitToShell;
  234.     end;
  235. END;
  236.  
  237. {$S Initialize}
  238. PROCEDURE Initialize;
  239.  
  240. VAR
  241.     menuBar            : Handle;
  242.     window            : WindowPtr;
  243.     ignoreError        : OSErr;
  244.     total, contig    : LongInt;
  245.     ignoreResult    : BOOLEAN;
  246.     event            : EventRecord;
  247.     count            : INTEGER;
  248.  
  249. BEGIN
  250.     gInBackground := FALSE;
  251.  
  252.     InitGraf(@thePort);
  253.     InitFonts;
  254.     InitWindows;
  255.     InitMenus;
  256.     TEInit;
  257.     InitDialogs(NIL);
  258.     InitCursor;
  259.     
  260.     {Call EventAvail so that we don't lose some important events.}
  261.     FOR count := 1 TO 3 DO
  262.         ignoreResult := EventAvail(everyEvent, event);
  263.  
  264.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  265.     SetMenuBar(menuBar);                    {install menus}
  266.     DisposHandle(menuBar);
  267.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  268.     DrawMenuBar;
  269.  
  270.     RememberWhereMyQueueIs;
  271. END; {Initialize}
  272.  
  273. {$S Main}
  274. PROCEDURE Terminate;
  275.  
  276. {Clean up the application and exits. We close all of the windows so that
  277.  they can update their documents, if any.}
  278. VAR
  279.     aWindow    : WindowPtr;
  280. BEGIN
  281.     WHILE FrontWindow <> NIL DO
  282.         DoCloseWindow(FrontWindow);            {close this window}
  283.     ExitToShell;                            {exit if no cancellation}
  284. END; {Terminate}
  285.  
  286.  
  287. {$S Main}
  288. PROCEDURE MakeNewWindow( thePicture : PicHandle; dateTime : LONGINT;
  289.     location : Rect; appName : Str255 );
  290. VAR
  291.     theWindow : WindowPtr;
  292.     timeNDateString : Str255;
  293. BEGIN
  294.     if location.top < GetMBarHeight + 20 then
  295.         OffsetRect(location, 0, GetMBarHeight + 20 - location.top);
  296.     IUTimeString(dateTime, true, timeNDateString);
  297.     timeNDateString := concat(appName,' ',timeNDateString);
  298.     theWindow := NewWindow (nil, location, timeNDateString, false, documentProc, 
  299.         WindowPtr(-1), true, 0);
  300.     SetWindowPic(theWindow, thePicture);
  301.     ForceOnScreen( theWindow );
  302.     ShowWindow( theWindow );
  303. END;
  304.  
  305. TYPE
  306.     ourQElem = record
  307.         qLink: QElemPtr;
  308.         qType: INTEGER;
  309.         picture: PicHandle;
  310.         dateTime: LONGINT;
  311.         location: Rect;
  312.         appName: Str255;
  313.     end;
  314.  
  315. {$S Main}
  316.  
  317. PROCEDURE GetPicturesFromQueue;
  318. VAR
  319.     err : OSErr;
  320.     myQElement : ^ourQElem;
  321. BEGIN
  322.     QElemPtr(myQElement) := gSharedGlobals^.q.qHead;
  323.     if gSharedGlobals^.nmReqPtr <> NIL then BEGIN
  324.         err := NMRemove(gSharedGlobals^.nmReqPtr);
  325.         gSharedGlobals^.nmReqPtr := NIL;
  326.     END;
  327.     if myQElement <> NIL then begin
  328.         err := Dequeue(QElemPtr(myQElement), @gSharedGlobals^.q);
  329.         if (err = noErr) THEN BEGIN
  330.             MakeNewWindow( myQElement^.picture, myQElement^.dateTime,
  331.                 myQElement^.location, myQElement^.appName);
  332.             SetZone(SystemZone);
  333.             DisposePtr(Ptr(myQElement));
  334.             SetZone(ApplicationZone);
  335.         END;
  336.     END;
  337. END;
  338.  
  339.  
  340. {$S Main}
  341. PROCEDURE AdjustMenus;
  342.  
  343. {Enable and disable menus based on the current state.
  344.  The user can only select enabled menu items. We set up all the menu items
  345.  before calling MenuSelect or MenuKey, since these are the only times that
  346.  a menu item can be selected. Note that MenuSelect is also the only time
  347.  the user will see menu items. This approach to deciding what enable/
  348.  disable state a menu item has the advantage of concentrating all the decision-
  349.  making in one routine, as opposed to being spread throughout the application.
  350.  Other application designs may take a different approach that may or may not be
  351.  just as valid.}
  352.  
  353. VAR
  354.     window            : WindowPtr;
  355.     menu            : MenuHandle;
  356.  
  357. BEGIN
  358.     window := FrontWindow;
  359.  
  360.     menu := GetMHandle(mFile);
  361.     IF IsDAWindow(window) THEN                {we can allow desk accessories to be closed from the menu}
  362.         EnableItem(menu, iClose)
  363.     ELSE
  364.         EnableItem(menu, iClose);
  365.  
  366.     EnableItem(menu, iNew);
  367.     
  368.     menu := GetMHandle(mEdit);
  369.     IF IsDAWindow(window) THEN BEGIN        {a desk accessory might need the edit menu}
  370.         EnableItem(menu, iUndo);
  371.         EnableItem(menu, iCut);
  372.         EnableItem(menu, iCopy);
  373.         EnableItem(menu, iPaste);
  374.         EnableItem(menu, iClear);
  375.     END ELSE BEGIN                            {but we know we do not}
  376.         DisableItem(menu, iUndo);
  377.         DisableItem(menu, iCut);
  378.         DisableItem(menu, iCopy);
  379.         DisableItem(menu, iClear);
  380.         DisableItem(menu, iPaste);
  381.     END;
  382. END; {AdjustMenus}
  383.  
  384.  
  385. {$S Main}
  386. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  387.  
  388. {This is called when an item is chosen from the menu bar (after calling
  389.  MenuSelect or MenuKey). It performs the right operation for each command.
  390.  It is good to have both the result of MenuSelect and MenuKey go to
  391.  one routine like this to keep everything organized.}
  392.  
  393. VAR
  394.     menuID            : INTEGER;        {the resource ID of the selected menu}
  395.     menuItem        : INTEGER;        {the item number of the selected menu}
  396.     itemHit            : INTEGER;
  397.     daName            : Str255;
  398.     daRefNum        : INTEGER;
  399.     handledByDA        : BOOLEAN;
  400.     ignore            : BOOLEAN;
  401.     dateTime        : LONGINT;
  402.  
  403. BEGIN
  404.     menuID := HiWrd(menuResult);    {use built-ins (for efficiency)...}
  405.     menuItem := LoWrd(menuResult);    {to get menu item number and menu number}
  406.     CASE menuID OF
  407.         mApple:
  408.             CASE menuItem OF
  409.                 iAbout:                {bring up alert for About}
  410.                     itemHit := Alert(rAboutAlert, NIL);
  411.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  412.                     GetItem(GetMHandle(mApple), menuItem, daName);
  413.                     daRefNum := OpenDeskAcc(daName);
  414.                 END;
  415.             END;
  416.         mFile:
  417.             CASE menuItem OF
  418.                 iNew: BEGIN
  419.                 {    GetDateTime(dateTime);
  420.                     MakeNewWindow(GetPicture(128), dateTime, nil, "");}
  421.                 END;
  422.                 iClose:
  423.                     if FrontWindow <> NIL then
  424.                         DoCloseWindow(FrontWindow);
  425.                 iQuit:
  426.                     Terminate;
  427.             END;
  428.         mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  429.             handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  430.     END;
  431.     HiliteMenu(0);                    {unhighlight what MenuSelect (or MenuKey) hilited}
  432. END; {DoMenuCommand}
  433.  
  434.  
  435. {$S Main}
  436. PROCEDURE DrawWindow(window: WindowPtr);
  437.  
  438. {Draw the contents of the application window. We do some drawing in color, using
  439.  Classic QuickDraw's color capabilities. This will be black and white on old
  440.  machines, but color on color machines. At this point, the window's visRgn is
  441.  set to allow drawing only where it needs to be done.}
  442.  
  443. BEGIN
  444.     SetPort(window);
  445.  
  446. END; {DrawWindow}
  447.  
  448.  
  449. {$S Main}
  450. PROCEDURE DoContentClick(window: WindowPtr; event: EventRecord);
  451. BEGIN
  452. END; {DoContentClick}
  453.  
  454.  
  455. {$S Main}
  456. PROCEDURE DoUpdate(window: WindowPtr);
  457.  
  458. {This is called when an update event is received for a window.
  459.  It calls DrawWindow to draw the contents of an application window.
  460.  As an effeciency measure that does not have to be followed, it
  461.  calls the drawing routine only if the visRgn is non-empty. This
  462.  will handle situations where calculations for drawing or drawing
  463.  itself is very time-consuming.}
  464.  
  465. BEGIN
  466.     IF IsAppWindow(window) THEN BEGIN
  467.         BeginUpdate(window);                    {sets up the visRgn, clears updateRgn}
  468.         IF NOT EmptyRgn(window^.visRgn) THEN    {draw if updating needs to be done}
  469.             DrawWindow(window);
  470.         EndUpdate(window);                        {restores the visRgn}
  471.     END;
  472. END; {DoUpdate}
  473.  
  474.  
  475. {$S Main}
  476. PROCEDURE DoActivate(window: WindowPtr; becomingActive: BOOLEAN);
  477.  
  478. {This is called when a window is activated or deactivated.
  479.  In Sample, the Window Manager's handling of activate and
  480.  deactivate events is sufficient. Other applications may have
  481.  TextEdit records, controls, lists, etc., to activate/deactivate.}
  482.  
  483. BEGIN
  484.     IF IsAppWindow(window) THEN
  485.         IF becomingActive THEN
  486.             {do whatever you need to at activation}
  487.         ELSE
  488.             {do whatever you need to at deactivation};
  489. END; {DoActivate}
  490.  
  491.  
  492. {$S Main}
  493. PROCEDURE GetGlobalMouse(VAR mouse: Point);
  494.  
  495. {Get the global coordinates of the mouse. When you call OSEventAvail
  496.  it will return either a pending event or a null event. In either case,
  497.  the where field of the event record will contain the current position
  498.  of the mouse in global coordinates and the modifiers field will reflect
  499.  the current state of the modifiers. Another way to get the global
  500.  coordinates is to call GetMouse and LocalToGlobal, but that requires
  501.  being sure that thePort is set to a valid port.}
  502.  
  503. VAR
  504.     event    : EventRecord;
  505.     
  506. BEGIN
  507.     IF OSEventAvail(kNoEvents, event) THEN;    {we aren't interested in any events}
  508.     mouse := event.where;                    {just the mouse position}
  509. END;
  510.  
  511.  
  512. {$S Main}
  513. PROCEDURE AdjustCursor(mouse: Point; region: RgnHandle);
  514.  
  515. {Change the cursor's shape, depending on its position. This also calculates the region
  516.  where the current cursor resides (for WaitNextEvent). If the mouse is ever outside of
  517.  that region, an event is generated, causing this routine to be called. This
  518.  allows us to change the region to the region the mouse is currently in. If
  519.  there is more to the event than just “the mouse moved”, we get called before the
  520.  event is processed to make sure the cursor is the right one. In any (ahem) event,
  521.  this is called again before we fall back into WNE.}
  522.  
  523.  
  524. VAR
  525.     window                : WindowPtr;
  526.     arrowRgn            : RgnHandle;
  527.     plusRgn                : RgnHandle;
  528.     globalPortRect        : Rect;
  529.     
  530.  
  531. BEGIN
  532.     window := FrontWindow;    {we only adjust the cursor when we are in front}
  533.     IF (NOT gInBackground) AND (NOT IsDAWindow(window)) THEN BEGIN
  534.         {calculate regions for different cursor shapes}
  535.         arrowRgn := NewRgn;
  536.         plusRgn := NewRgn;
  537.  
  538.         {start with a big, big rectangular region}
  539.         {1.01 - changed to kExtremeNeg and kExtremePos for consistency}
  540.         SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg,
  541.                             kExtremePos, kExtremePos);
  542.  
  543.         {calculate plusRgn}
  544.         IF IsAppWindow(window) THEN BEGIN
  545.             SetPort(window);            {make a global version of the portRect}
  546.             SetOrigin(-window^.portBits.bounds.left, -window^.portBits.bounds.top);
  547.             globalPortRect := window^.portRect;
  548.             RectRgn(plusRgn, globalPortRect);
  549.             SectRgn(plusRgn, window^.visRgn, plusRgn);
  550.             SetOrigin(0, 0);
  551.         END;
  552.  
  553.         {subtract other regions from arrowRgn}
  554.         DiffRgn(arrowRgn, plusRgn, arrowRgn);
  555.  
  556.         {change the cursor and the region parameter}
  557.         IF PtInRgn(mouse, plusRgn) THEN BEGIN
  558.             SetCursor(GetCursor(plusCursor)^^);
  559.             CopyRgn(plusRgn, region);
  560.         END ELSE BEGIN
  561.             SetCursor(arrow);
  562.             CopyRgn(arrowRgn, region);
  563.         END;
  564.  
  565.         {get rid of our local regions}
  566.         DisposeRgn(arrowRgn);
  567.         DisposeRgn(plusRgn);
  568.     END;
  569. END; {AdjustCursor}
  570.  
  571.  
  572. {$S Main}
  573. PROCEDURE DoEvent(event: EventRecord);
  574.  
  575. {Do the right thing for an event. Determine what kind of event it is, and call
  576.  the appropriate routines.}
  577.  
  578. VAR
  579.     part, err    : INTEGER;
  580.     window        : WindowPtr;
  581.     hit            : BOOLEAN;
  582.     key            : CHAR;
  583.     aPoint        : Point;
  584.  
  585. BEGIN
  586.     CASE event.what OF
  587.         mouseDown: BEGIN
  588.             part := FindWindow(event.where, window);
  589.             CASE part OF
  590.                 inMenuBar: BEGIN            {process the menu command}
  591.                     AdjustMenus;
  592.                     DoMenuCommand(MenuSelect(event.where));
  593.                 END;
  594.                 inSysWindow:                {let the system handle the mouseDown}
  595.                     SystemClick(event, window);
  596.                 inContent:
  597.                     IF window <> FrontWindow THEN BEGIN
  598.                         SelectWindow(window);
  599.                         {DoEvent(event);}    {use this line for "do first click"}
  600.                     END ELSE
  601.                         DoContentClick(window, event);
  602.                 inDrag:                        {pass screenBits.bounds to get all gDevices}
  603.                     DragWindow(window, event.where, screenBits.bounds);
  604.                 inGrow:;
  605.                 inGoAway:
  606.                     IF TrackGoAway(window, event.where) THEN
  607.                         DoCloseWindow(window);
  608.                 inZoomIn, inZoomOut:;
  609.             END;
  610.         END;
  611.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  612.             key := CHR(BAnd(event.message, charCodeMask));
  613.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN    {Command key down}
  614.                 IF event.what = keyDown THEN BEGIN
  615.                     AdjustMenus;            {enable/disable/check menu items properly}
  616.                     DoMenuCommand(MenuKey(key));
  617.                 END;
  618.         END;                                {call DoActivate with the window and...}
  619.         activateEvt:                        {TRUE for activate, FALSE for deactivate}
  620.             DoActivate(WindowPtr(event.message), BAnd(event.modifiers, activeFlag) <> 0);
  621.         updateEvt:                          {call DoUpdate with the window to update}
  622.             BEGIN
  623.                 DoUpdate(WindowPtr(event.message));
  624.             END;
  625.         {1.01 - It is not a bad idea to at least call DIBadMount in response
  626.          to a diskEvt, so that the user can format a floppy.}
  627.         diskEvt:
  628.             IF HiWrd(event.message) <> noErr THEN BEGIN
  629.                 SetPt(aPoint, kDILeft, kDITop);
  630.                 err := DIBadMount(aPoint, event.message);
  631.             END;
  632.         kOSEvent:
  633.             CASE BAnd(BRotL(event.message, 8), $FF) OF    {high byte of message}
  634.                 kSuspendResumeMessage: BEGIN
  635.                     DoActivate(FrontWindow, NOT gInBackground);
  636.                 END;
  637.             END;
  638.         nullEvent: BEGIN
  639.         END;
  640.     END;
  641. END; {DoEvent}
  642.  
  643.  
  644. {$S Main}
  645. PROCEDURE EventLoop;
  646.  
  647. {Get events forever, and handle them by calling DoEvent.
  648.  Get the events by calling WaitNextEvent, if it's available, otherwise
  649.  by calling GetNextEvent. Also call AdjustCursor each time through the loop.}
  650.  
  651. VAR
  652.     cursorRgn    : RgnHandle;
  653.     gotEvent    : BOOLEAN;
  654.     event        : EventRecord;
  655.     mouse        : Point;
  656.  
  657. BEGIN
  658.     cursorRgn := NewRgn;                {we’ll pass WNE an empty region the 1st time thru}
  659.     REPEAT
  660.         GetPicturesFromQueue;
  661.         
  662.         IF gHasWaitNextEvent THEN BEGIN    {put us 'asleep' forever under MultiFinder}
  663.             GetGlobalMouse(mouse);        {since we might go to sleep}
  664.             AdjustCursor(mouse, cursorRgn);
  665.             gotEvent := WaitNextEvent(everyEvent, event, 10, cursorRgn);
  666.         END ELSE BEGIN
  667.             SystemTask;                    {must be called if using GetNextEvent}
  668.             gotEvent := GetNextEvent(everyEvent, event);
  669.         END;
  670.         IF gotEvent THEN BEGIN
  671.             AdjustCursor(event.where, cursorRgn);    {make sure we have the right cursor}
  672.             DoEvent(event);
  673.         END;
  674.     UNTIL FALSE;                        {loop forever; we quit through an ExitToShell}
  675. END; {EventLoop}
  676.  
  677.  
  678. PROCEDURE _DataInit; EXTERNAL;
  679.  
  680. {This routine is part of the MPW runtime library. This external
  681.  reference to it is done so that we can unload its segment, %A5Init.}
  682.  
  683. {$S Main}
  684. BEGIN
  685.     UnloadSeg(@_DataInit);    {note that _DataInit must not be in Main!}
  686.     
  687.     {1.01 - call to ForceEnvirons removed}
  688.     {If you have stack requirements that differ from the default,
  689.      then you could use SetApplLimit to increase StackSpace at 
  690.      this point, before calling MaxApplZone.}
  691.      
  692.     MaxApplZone;            {expand the heap so code segments load at the top}
  693.  
  694.     Initialize;                {initialize the program}
  695.     UnloadSeg(@Initialize);    {note that Initialize must not be in Main!}
  696.  
  697.     EventLoop;                {call the main event loop}
  698. END.
  699.